package ace.module.common.core.impl.controller.rest.admin;

import ace.cmp.core.model.R;
import ace.cmp.rest.spring.data.jdbc.querydsl.core.controller.admin.AbstractAdminRestController;
import ace.cmp.util.AceAssertUtils;
import ace.module.common.api.model.input.TenantAndAppTypeInput;
import ace.module.common.core.impl.manager.ApplicationManager;
import ace.module.common.core.impl.manager.EntityOfAppManager;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import java.io.Serializable;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import lombok.AccessLevel;
import lombok.Getter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.web.PageableDefault;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;

/**
 * 管理员租户端-管理API 用于管理员管理单一租户相关数据.
 *
 * @author Caspar
 */
public class AdminTenantEntityOfAppController<EntityT, IdT, AppEntityT>
    extends AbstractAdminRestController<EntityT, IdT> {

  public static final String ADMIN_TENANT_SAVE_URI = "/admin/tenant/save";
  public static final String ADMIN_TENANT_SAVE_NOT_NULL_FIELD_URI = "/admin/tenant/saveNotNullField";
  public static final String ADMIN_TENANT_ADD_URI = "/admin/tenant/add";
  public static final String ADMIN_TENANT_ADD_NOT_NULL_FIELD_URI = "/admin/tenant/addNotNullField";
  public static final String ADMIN_TENANT_MODIFY_URI = "/admin/tenant/modify";
  public static final String ADMIN_TENANT_MODIFY_NOT_NULL_FIELD_URI = "/admin/tenant/modifyNotNullField";
  public static final String ADMIN_TENANT_REMOVE_BY_ID_URI = "/admin/tenant/remove";
  public static final String ADMIN_TENANT_REMOVE_LOGIC_BY_ID_URI = "/admin/tenant/removeLogic";
  public static final String ADMIN_TENANT_GET_BY_ID_URI = "/admin/tenant/get";
  public static final String ADMIN_TENANT_GET_LIST_BY_ID_IN_URI = "/admin/tenant/list";
  public static final String ADMIN_TENANT_GET_MAP_BY_ID_IN_URI = "/admin/tenant/map";
  public static final String ADMIN_TENANT_SEARCH_URI = "/admin/tenant/search";

  @Autowired
  @Getter(AccessLevel.PROTECTED)
  private ApplicationManager<AppEntityT, IdT, ?> applicationManager;

  @Autowired
  @Getter(AccessLevel.PROTECTED)
  private EntityOfAppManager<EntityT, IdT, ?> entityOfAppManager;


  @Operation(summary = "管理端租户管理-保存,新增或者修改")
  @PostMapping(ADMIN_TENANT_SAVE_URI)
  public R<IdT> adminTenantSave(@Valid @NotNull @RequestBody TenantAndAppTypeInput<EntityT> input) {
    this.getSecurityManager().checkWriteAuthority();
    EntityT entity = input.getData();
    if (this.getManager().isNew(entity)) {
      this.setAndCheckEntityAppId(entity, input.getTenantId(), input.getType());
    } else {
      this.checkEntityAppIdEqualByTenantIdAndType(entity, input.getTenantId(), input.getType());
    }

    IdT id = this.getManager().saveIgnore(entity, true);
    return R.ok(id);
  }

  @Operation(summary = "管理端租户管理-保存,新增或者修改,不为null的字段")
  @PostMapping(ADMIN_TENANT_SAVE_NOT_NULL_FIELD_URI)
  public R<IdT> adminTenantSaveNotNullField(@Valid @NotNull @RequestBody TenantAndAppTypeInput<EntityT> input) {
    this.getSecurityManager().checkWriteAuthority();
    EntityT entity = input.getData();
    if (this.getManager().isNew(entity)) {
      this.setAndCheckEntityAppId(entity, input.getTenantId(), input.getType());
    } else {
      this.checkEntityAppIdEqualByTenantIdAndType(entity, input.getTenantId(), input.getType());
    }
    IdT id = this.getManager().saveIgnore(entity, false);
    return R.ok(id);
  }

  @Operation(summary = "管理端租户管理-新增")
  @PostMapping(ADMIN_TENANT_ADD_URI)
  public R<IdT> adminTenantAdd(@Valid @NotNull @RequestBody TenantAndAppTypeInput<EntityT> input) {
    this.getSecurityManager().checkWriteAuthority();
    EntityT entity = input.getData();
    this.setAndCheckEntityAppId(entity, input.getTenantId(), input.getType());
    IdT id = this.getManager().addIgnore(entity, true);
    return R.ok(id);
  }

  @Operation(summary = "管理端租户管理-新增,不为null的字段")
  @PostMapping(ADMIN_TENANT_ADD_NOT_NULL_FIELD_URI)
  public R<IdT> adminTenantAddNotNullField(@Valid @NotNull @RequestBody TenantAndAppTypeInput<EntityT> input) {
    this.getSecurityManager().checkWriteAuthority();
    EntityT entity = input.getData();
    this.setAndCheckEntityAppId(entity, input.getTenantId(), input.getType());
    IdT id = this.getManager().addIgnore(entity, false);
    return R.ok(id);
  }

  @Operation(summary = "管理端租户管理-修改")
  @PostMapping(ADMIN_TENANT_MODIFY_URI)
  public R<IdT> adminTenantModify(@Valid @NotNull @RequestBody TenantAndAppTypeInput<EntityT> input) {
    this.getSecurityManager().checkWriteAuthority();
    EntityT entity = input.getData();
    this.checkEntityAppIdEqualByTenantIdAndType(entity, input.getTenantId(), input.getType());
    IdT id = this.getManager().modifyIgnore(entity, Boolean.TRUE);
    return R.ok(id);
  }

  @Operation(summary = "管理端租户管理-修改,不为null的字段")
  @PostMapping(ADMIN_TENANT_MODIFY_NOT_NULL_FIELD_URI)
  public R<IdT> adminTenantModifyNotNullField(@Valid @NotNull @RequestBody TenantAndAppTypeInput<EntityT> input) {
    this.getSecurityManager().checkWriteAuthority();
    EntityT entity = input.getData();
    this.checkEntityAppIdEqualByTenantIdAndType(entity, input.getTenantId(), input.getType());
    IdT id = this.getManager().modifyIgnore(entity, Boolean.FALSE);
    return R.ok(id);
  }

  @Operation(summary = "管理端租户管理-删除")
  @PostMapping(ADMIN_TENANT_REMOVE_BY_ID_URI)
  public R<Boolean> adminTenantRemove(
      @Valid @NotNull @NotEmpty @RequestBody TenantAndAppTypeInput<List<IdT>> input) {
    this.getSecurityManager().checkRemoveAuthority();
    Serializable appId = this.getApplicationManager().getIdAndCheckByOutIdAndOutType(input.getTenantId().toString(), input.getType());
    boolean isSuccess = this.getEntityOfAppManager().removeByAppIdAndIdIn(appId, input.getData());
    return R.ok(isSuccess);
  }

  @Operation(summary = "管理端租户管理-逻辑删除")
  @PostMapping(ADMIN_TENANT_REMOVE_LOGIC_BY_ID_URI)
  public R<Boolean> adminTenantRemoveLogic(
      @Valid @NotNull @NotEmpty @RequestBody TenantAndAppTypeInput<List<IdT>> input) {
    this.getSecurityManager().checkRemoveAuthority();
    Serializable appId = this.getApplicationManager().getIdAndCheckByOutIdAndOutType(input.getTenantId().toString(), input.getType());
    boolean isSuccess = this.getEntityOfAppManager().removeLogicByAppIdIdAndIdIn(appId, input.getData());
    return R.ok(isSuccess);
  }

  @Operation(summary = "管理端租户管理-获取明细")
  @GetMapping(ADMIN_TENANT_GET_BY_ID_URI)
  public R<EntityT> adminTenantGetById(
      @Parameter(description = "租户id") @Valid @NotEmpty @RequestParam("tenantId") String tenantId,
      @Parameter(description = "数据类型") @Valid @NotEmpty @RequestParam("type") String type,
      @Valid @NotNull @RequestParam("id") IdT id) {
    this.getSecurityManager().checkReadAuthority();
    Serializable appId = this.getApplicationManager().getIdByOutIdAndOutType(tenantId, type);
    if (appId == null) {
      return R.ok();
    }
    EntityT entityDto = this.getEntityOfAppManager().getByAppIdAndId(appId, id);
    return R.ok(entityDto);
  }

  @Operation(summary = "管理端租户管理-根据id批量获取")
  @GetMapping(ADMIN_TENANT_GET_LIST_BY_ID_IN_URI)
  public R<List<EntityT>> adminTenantGetListByIdIn(
      @Parameter(description = "租户id") @Valid @NotEmpty @RequestParam("tenantId") String tenantId,
      @Parameter(description = "数据类型") @Valid @NotEmpty @RequestParam("type") String type,
      @Valid @NotNull @NotEmpty @RequestParam("ids") List<IdT> ids) {
    this.getSecurityManager().checkReadAuthority();
    Serializable appId = this.getApplicationManager().getIdByOutIdAndOutType(tenantId, type);
    if (appId == null) {
      return R.ok();
    }
    List<EntityT> entityDtoList = this.getEntityOfAppManager().getByAppIdAndIds(appId, ids);
    return R.ok(entityDtoList);
  }

  @Operation(summary = "管理端租户管理-根据id批量获取,返回Map数据结构，key:实体id,value:实体")
  @GetMapping(ADMIN_TENANT_GET_MAP_BY_ID_IN_URI)
  public R<Map<IdT, EntityT>> adminTenantGetMapByIdIn(
      @Parameter(description = "租户id") @Valid @NotEmpty @RequestParam("tenantId") String tenantId,
      @Parameter(description = "数据类型") @Valid @NotEmpty @RequestParam("type") String type,
      @Valid @NotNull @NotEmpty @RequestParam("ids") List<IdT> ids) {
    this.getSecurityManager().checkReadAuthority();
    Serializable appId = this.getApplicationManager().getIdByOutIdAndOutType(tenantId, type);
    if (appId == null) {
      return R.ok();
    }
    Map<IdT, EntityT> entityDtoMap = (Map<IdT, EntityT>) this.getEntityOfAppManager().getMapByAppIdAndIds(appId, ids);
    return R.ok(entityDtoMap);
  }

  @Operation(summary = "管理端租户管理-搜索", description = "rsql语法格式分页搜索")
  @GetMapping(ADMIN_TENANT_SEARCH_URI)
  public R<Page<EntityT>> adminTenantSearch(
      @Parameter(description = "租户id") @Valid @NotEmpty @RequestParam(value = "tenantId", required = true) String tenantId,
      @Parameter(description = "数据类型") @Valid @NotEmpty @RequestParam("type") String type,
      @Parameter(description = "rsql语法格式查询条件") @RequestParam(value = "query", required = false) String query,
      @PageableDefault Pageable pageable) {
    this.getSecurityManager().checkReadAuthority();
    Serializable appId = this.getApplicationManager().getIdByOutIdAndOutType(tenantId, type);
    if (appId == null) {
      return R.ok();
    }
    Page<EntityT> entityDtoPage = this.getEntityOfAppManager().searchByAppIdAndRsql(appId, query, pageable);
    return R.ok(entityDtoPage);
  }


  protected void setAndCheckEntityAppId(EntityT data, Long tenantId, String type) {
    Serializable appId = this.getApplicationManager().getIdAndCheckByOutIdAndOutType(
        tenantId.toString(),
        type
    );
    this.getEntityOfAppManager().setEntityAppId(data, appId);
  }

  /**
   * 检查实体的appId是否等于租户id与类型查询到的appId
   * @param entity 实体
   * @param tenantId 租户id
   * @param type 类型
   */
  protected void checkEntityAppIdEqualByTenantIdAndType(EntityT entity, Long tenantId, String type) {
    Serializable appId = this.getApplicationManager().getIdAndCheckByOutIdAndOutType(
        tenantId.toString(),
        type
    );
    Serializable entityAppId = this.getEntityOfAppManager().getEntityAppId(entity);
    String msg = String.format("实体的应用id[%s]不等于租户的应用id[%s]", entityAppId, appId);
    AceAssertUtils.isFalse(Objects.equals(entityAppId, appId), msg);
  }
}
